﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace LabelPrintSystem
{
  /// <summary>
  /// ZPL帮助类
  /// </summary>
  public class ClsZebraHelper
  {
    /*
     * 打印中文先引用Fnthex32.dll
      dll控件常规安装方法（仅供参考）：
      下面是32系统的注册bat文件
      可将下面的代码保存为“注册.bat“，放到dll目录，就会自动完成dll注册(win98不支持)。
      @echo 开始注册
      copy Fnthex32.dll %windir%\system32\
      regsvr32 %windir%\system32\Fnthex32.dll /s
      @echo Fnthex32.dll注册成功
      @pause
      下面是64系统的注册bat文件
      @echo 开始注册
      copy Fnthex32.dll %windir%\SysWOW64\
      regsvr32 %windir%\SysWOW64\Fnthex32.dll /s
      @echo Fnthex32.dll注册成功
      @pause
     *
     *
      ZebraHelper zh = new ZebraHelper();
      StringBuilder builder = new StringBuilder();
      builder.AppendLine(zh.ZPL_Start());
      builder.AppendLine(zh.ZPL_PageSet(40, 80));
      builder.AppendLine(zh.ZPL_DrawCHText("上善若水 厚德载物", "宋体", 40, 40, 0, 32, 0, 1, 0));
      builder.AppendLine(zh.ZPL_DrawBarcode(40, 150, 3, 2, 40, "111112222233333"));
      builder.AppendLine(zh.ZPL_DrawENText("111112222233333", "A", 30, 205, "N", 30, 50));
      builder.AppendLine(zh.ZPL_DrawRectangle(20,20,2,700,700));
      builder.AppendLine(zh.ZPL_End());
      string a = builder.ToString();
      //打印
      zh.CmdDos("c:\\c.txt", a);
     */
    public string ZPL_Start()
    {
      StringBuilder builder = new StringBuilder();
      builder.AppendLine("^XA"); //指令块的开始
      builder.AppendLine("^MD30"); //MD是设置色带颜色的深度
      return builder.ToString();
    }
    public string ZPL_End()
    {
      StringBuilder builder = new StringBuilder();
      builder.AppendLine("^XZ"); //指令块的结束
      return builder.ToString();
    }
    /// <summary>
    /// 设置打印标签纸边距
    /// </summary>
    /// <param name="printX">标签纸边距x坐标</param>
    /// <param name="printY">标签纸边距y坐标</param>
    /// <returns></returns>
    public string ZPL_PageSet(int printX, int printY)
    {
      StringBuilder builder = new StringBuilder();
      builder.AppendLine("^LH" + printX + "," + printY); //定义条码纸边距 80 10
      return builder.ToString();
    }
    /// <summary>
    /// 打印凭条设置
    /// </summary>
    /// <param name="width">凭条宽度</param>
    /// <param name="height">凭条高度</param>
    /// <returns>返回ZPL命令</returns>
    public string ZPL_SetLabel(int width, int height)
    {
      //ZPL条码设置命令：^PW640^LL480
      string sReturn = "^PW{0}^LL{1}";
      return string.Format(sReturn, width, height);
    }
    /// <summary>
    /// 打印矩形
    /// </summary>
    /// <param name="px">起点X坐标</param>
    /// <param name="py">起点Y坐标</param>
    /// <param name="thickness">边框宽度</param>
    /// <param name="width">矩形宽度，0表示打印一条竖线</param>
    /// <param name="height">矩形高度，0表示打印一条横线</param>
    /// <returns>返回ZPL命令</returns>
    public string ZPL_DrawRectangle(int px, int py, int thickness, int width, int height)
    {
      //ZPL矩形命令：^FO50,50^GB300,200,2^FS
      string sReturn = "^FO{0},{1}^GB{3},{4},{2}^FS";
      return string.Format(sReturn, px, py, thickness, width, height);
    }
    /// <summary>
    /// 打印英文
    /// </summary>
    /// <param name="EnText">待打印文本</param>
    /// <param name="ZebraFont">打印机字体 A-Z</param>
    /// <param name="px">起点X坐标</param>
    /// <param name="py">起点Y坐标</param>
    /// <param name="Orient">旋转角度N = normal，R = rotated 90 degrees (clockwise)，I = inverted 180 degrees，B = read from bottom up, 270 degrees</param>
    /// <param name="Height">字体高度</param>
    /// <param name="Width">字体宽度</param>
    /// <returns>返回ZPL命令</returns>
    public string ZPL_DrawENText(string EnText,string ZebraFont, int px, int py, string Orient, int Height, int Width)
    {
      //ZPL打印英文命令：^FO50,50^A0N,32,25^FDZEBRA^FS
      string sReturn = "^FO{1},{2}^A" + ZebraFont + "{3},{4},{5}^FD{0}^FS";
      return string.Format(sReturn, EnText, px, py, Orient, Height, Width);
    }
    /// <summary>
    /// 中文处理,返回ZPL命令
    /// </summary>
    /// <param name="ChineseText">待转变中文内容</param>
    /// <param name="FontName">字体名称</param>
    /// <param name="startX">X坐标</param>
    /// <param name="startY">Y坐标</param>
    /// <param name="Orient">旋转角度0,90,180,270</param>
    /// <param name="Height">字体高度</param>
    /// <param name="Width">字体宽度，通常是0</param>
    /// <param name="IsBold">1 变粗,0 正常</param>
    /// <param name="IsItalic">1 斜体,0 正常</param>
    /// <returns></returns>
    public string ZPL_DrawCHText(string ChineseText, string FontName, int startX, int startY, int Orient, int Height, int Width, int IsBold, int IsItalic)
    {
      StringBuilder sResult = new StringBuilder();
      StringBuilder hexbuf = new StringBuilder(21 * 1024);
      int count = ClsZebraHelper.GETFONTHEX(ChineseText, FontName, Orient, Height, Width, IsBold, IsItalic, hexbuf);
      if (count > 0)
      {
        string sEnd = "^FO" + startX.ToString() + "," + startY.ToString() + "^XGOUTSTR" + ",1,2^FS ";
        sResult.AppendLine(hexbuf.ToString().Replace("OUTSTR01", "OUTSTR") + sEnd);
      }
      return sResult.ToString();
    }
    /// <summary>
    /// 打印条形码（128码）
    /// </summary>
    /// <param name="px">起点X坐标</param>
    /// <param name="py">起点Y坐标</param>
    /// <param name="width">基本单元宽度 1-10</param>
    /// <param name="ratio">宽窄比 2.0-3.0 增量0.1</param>
    /// <param name="barheight">条码高度</param>
    /// <param name="barcode">条码内容</param>
    /// <returns>返回ZPL命令</returns>
    public string ZPL_DrawBarcode(int px, int py, int width, int ratio, int barheight, string barcode)
    {
      //ZPL打印英文命令：^FO50,260^BY1,2^BCN,100,Y,N^FDSMJH2000544610^FS
      string sReturn = "^FO{0},{1}^BY{2},{3}^BCN,{4},N,N^FD{5}^FS";
      return string.Format(sReturn, px, py, width, ratio, barheight, barcode);
    }
    /// <summary>
    /// 中文处理
    /// </summary>
    /// <param name="ChineseText">待转变中文内容</param>
    /// <param name="FontName">字体名称</param>
    /// <param name="Orient">旋转角度0,90,180,270</param>
    /// <param name="Height">字体高度</param>
    /// <param name="Width">字体宽度，通常是0</param>
    /// <param name="IsBold">1 变粗,0 正常</param>
    /// <param name="IsItalic">1 斜体,0 正常</param>
    /// <param name="ReturnPicData">返回的图片字符</param>
    /// <returns></returns>
    [DllImport("fnthex32.dll")]
    public static extern int GETFONTHEX(
      string ChineseText,
      string FontName,
      int Orient,
      int Height,
      int Width,
      int IsBold,
      int IsItalic,
      StringBuilder ReturnPicData);
    /// <summary>
    /// 中文处理
    /// </summary>
    /// <param name="ChineseText">待转变中文内容</param>
    /// <param name="FontName">字体名称</param>
    /// <param name="FileName">返回的图片字符重命</param>
    /// <param name="Orient">旋转角度0,90,180,270</param>
    /// <param name="Height">字体高度</param>
    /// <param name="Width">字体宽度，通常是0</param>
    /// <param name="IsBold">1 变粗,0 正常</param>
    /// <param name="IsItalic">1 斜体,0 正常</param>
    /// <param name="ReturnPicData">返回的图片字符</param>
    /// <returns></returns>
    [DllImport("fnthex32.dll")]
    public static extern int GETFONTHEX(
               string ChineseText,
               string FontName,
               string FileName,
               int Orient,
               int Height,
               int Width,
               int IsBold,
               int IsItalic,
               StringBuilder ReturnPicData);
    #region 扩展
    /// <summary>
    /// 毫米转像素 取整
    /// </summary>
    /// <param name="mm">毫米</param>
    /// <param name="dpi">打印DPI 如300</param>
    /// <returns></returns>
    public double mm2px(double mm, double dpi)
    {
      double px = (mm / 25.4) * dpi;
      return Math.Round(px, 0, MidpointRounding.AwayFromZero);
    }
    /// <summary>
    /// 像素转毫米 取整
    /// </summary>
    /// <param name="px">像素</param>
    /// <param name="dpi">打印DPI 如300</param>
    /// <returns></returns>
    public double px2mm(double px, double dpi)
    {
      //像素转换成毫米公式：(宽度像素/水平DPI)*25.4;
      double mm = (px / dpi) * 25.4;
      return Math.Round(mm, 0, MidpointRounding.AwayFromZero);
    }
    /// <summary>
    /// 生成zpl命令 且执行
    /// </summary>
    /// <param name="path">zpl文件路径</param>
    /// <param name="zpl">zpl命令</param>
    public void CmdDos(string path,string zpl)
    {
      FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
      StreamWriter sw = new StreamWriter(fs, Encoding.Default);//ANSI编码格式
      if (File.Exists(path))
      {
        sw.Write(zpl);
        sw.Flush();
        sw.Close();
      }
      fs.Close();
      RunCmd("print /d:COM1 " + path + " ");
    }
    /// <summary>
    /// 运行Dos命令
    /// </summary>
    /// <param name="command"></param>
    /// <returns></returns>
    private bool RunCmd(string command)
    {
      //实例一个Process类，启动一个独立进程
      Process p = new Process();
      //Process类有一个StartInfo属性，這個是ProcessStartInfo类，包括了一些属性和方法，下面我們用到了他的几个属性：
      p.StartInfo.FileName = "cmd.exe";//设定程序名
      p.StartInfo.Arguments = "/c " + command;//设定程式执行参数
      p.StartInfo.UseShellExecute = false;//关闭Shell的使用
      p.StartInfo.RedirectStandardInput = true;//重定向标准输入
      p.StartInfo.RedirectStandardOutput = true;//重定向标准输出
      p.StartInfo.RedirectStandardError = true;//重定向错误输出
      p.StartInfo.CreateNoWindow = true;//设置不显示窗口
      //p.StandardInput.WriteLine(command);//也可以用这种方式输入要执行的命令
      //p.StandardInput.WriteLine("exit");//不过要记得加上Exit要不然下一行程式执行的時候会当机
      try
      {
        p.Start();//开始进程
        return true;
      }
      catch
      {
      }
      finally
      {
        if (p != null)
          p.Close();
      }
      return false;
    }
    #endregion
  }



  public class ZebraGesigner
  {
      [StructLayout(LayoutKind.Sequential)]
      private struct OVERLAPPED
      {
          int Internal;
          int InternalHigh;
          int Offset;
          int OffSetHigh;
          int hEvent;
      }
      [DllImport("kernel32.dll")]
      private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
      [DllImport("kernel32.dll")]
      private static extern bool WriteFile(int hFile, byte[] lpBuffer, int nNumberOfBytesToWriter, out int lpNumberOfBytesWriten, out OVERLAPPED lpOverLapped);
      [DllImport("kernel32.dll")]
      private static extern bool CloseHandle(int hObject);
      [DllImport("fnthex32.dll")]
      public static extern int GETFONTHEX(string barcodeText, string fontName, int orient, int height, int width, int isBold, int isItalic, StringBuilder returnBarcodeCMD);
      private int iHandle;
      //打开LPT 端口  
      public bool Open()
      {
          iHandle = CreateFile("lpt1", 0x40000000, 0, 0, 3, 0, 0);
          if (iHandle != -1)
          {
              return true;
          }
          else
          {
              return false;
          }
      }
      //打印函数，参数为打印机的命令或者其他文本！  
      public bool Write(string MyString)
      {
          if (iHandle != -1)
          {
              int i;
              OVERLAPPED x;
              byte[] mybyte = System.Text.Encoding.Default.GetBytes(MyString);
              return WriteFile(iHandle, mybyte, mybyte.Length, out i, out x);
          }
          else
          {
              throw new Exception("端口未打开~！");
          }
      }
      //关闭打印端口  
      public bool Close()
      {
          return CloseHandle(iHandle);
      }

  }

  class RawPrinterHelper
  {
      // Structure and API declarions:  
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
      public class DOCINFOA
      {
          [MarshalAs(UnmanagedType.LPStr)]
          public string pDocName;
          [MarshalAs(UnmanagedType.LPStr)]
          public string pOutputFile;
          [MarshalAs(UnmanagedType.LPStr)]
          public string pDataType;
      }
      [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

      [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool ClosePrinter(IntPtr hPrinter);

      [DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

      [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool EndDocPrinter(IntPtr hPrinter);

      [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool StartPagePrinter(IntPtr hPrinter);

      [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool EndPagePrinter(IntPtr hPrinter);

      [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
      public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

      // SendBytesToPrinter()  
      // When the function is given a printer name and an unmanaged array  
      // of bytes, the function sends those bytes to the print queue.  
      // Returns true on success, false on failure.  
      public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
      {
          Int32 dwError = 0, dwWritten = 0;
          IntPtr hPrinter = new IntPtr(0);
          DOCINFOA di = new DOCINFOA();
          bool bSuccess = false; // Assume failure unless you specifically succeed.  

          di.pDocName = "My C#.NET RAW Document";
          di.pDataType = "RAW";

          // Open the printer.  
          if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
          {
              // Start a document.  
              if (StartDocPrinter(hPrinter, 1, di))
              {
                  // Start a page.  
                  if (StartPagePrinter(hPrinter))
                  {
                      // Write your bytes.  
                      bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                      EndPagePrinter(hPrinter);
                  }
                  EndDocPrinter(hPrinter);
              }
              ClosePrinter(hPrinter);
          }
          // If you did not succeed, GetLastError may give more information  
          // about why not.  
          if (bSuccess == false)
          {
              dwError = Marshal.GetLastWin32Error();
          }
          return bSuccess;
      }

      public static bool SendFileToPrinter(string szPrinterName, string szFileName)
      {
          // Open the file.  
          FileStream fs = new FileStream(szFileName, FileMode.Open);
          // Create a BinaryReader on the file.  
          BinaryReader br = new BinaryReader(fs);
          // Dim an array of bytes big enough to hold the file's contents.  
          Byte[] bytes = new Byte[fs.Length];
          bool bSuccess = false;
          // Your unmanaged pointer.  
          IntPtr pUnmanagedBytes = new IntPtr(0);
          int nLength;

          nLength = Convert.ToInt32(fs.Length);
          // Read the contents of the file into the array.  
          bytes = br.ReadBytes(nLength);
          // Allocate some unmanaged memory for those bytes.  
          pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
          // Copy the managed byte array into the unmanaged array.  
          Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
          // Send the unmanaged bytes to the printer.  
          bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
          // Free the unmanaged memory that you allocated earlier.  
          Marshal.FreeCoTaskMem(pUnmanagedBytes);
          return bSuccess;
      }

      public static bool SendStringToPrinter(string szPrinterName, string szString)
      {
          IntPtr pBytes;
          Int32 dwCount;
          // How many characters are in the string?  
          dwCount = szString.Length;
          // Assume that the printer is expecting ANSI text, and then convert  
          // the string to ANSI text.  
          pBytes = Marshal.StringToCoTaskMemAnsi(szString);
          // Send the converted ANSI string to the printer.  
          SendBytesToPrinter(szPrinterName, pBytes, dwCount);
          Marshal.FreeCoTaskMem(pBytes);
          return true;
      }

      public string Print(string stuNo, string liuBookNo, string suoBookNum, string stuName)
      {
          if (string.IsNullOrEmpty(liuBookNo) || string.IsNullOrEmpty(suoBookNum))
          {
              return "参数错误,打印失败!";
          }
          StringBuilder tiaomaStr = new StringBuilder();
          tiaomaStr.AppendLine();
          tiaomaStr.AppendLine("N");
          tiaomaStr.AppendLine("B0,10,0,1,2,3,100,B,$" + suoBookNum + "$");
          tiaomaStr.AppendLine("A2050,10,5,9,1,1,N,$" + liuBookNo + "$");
          tiaomaStr.AppendLine("A0,160,0,8,1,1,N,$未知$");
          tiaomaStr.AppendLine("A0,210,0,8,1,1,N,$捐书人:" + stuName + "$");
          tiaomaStr.AppendLine("D15");
          tiaomaStr.AppendLine("P1");
          FileStream fs = null;
          try
          {
              string path = Application.StartupPath + "~/BooksManagement\\File\\tiaoma.txt";
              //string path = Server.MapPath("~/BooksManagement\\File\\tiaoma.txt");
              fs = new FileStream(path, FileMode.Create, FileAccess.Write);
              StreamWriter sw = new StreamWriter(fs, Encoding.Default);//ANSI编码格式  
              if (File.Exists(path))
              {
                  sw.Write(tiaomaStr.ToString().Replace('$', '"'));
                  //tiaomaStr.Clear(); //V2.0往上可用
                  sw.Flush();
                  sw.Close();
                  if (RunCmd("COPY " + path + " LPT1"))
                      return string.Empty;
                  else
                      return "参数错误,打印失败!";
              }
          }
          catch
          {
          }
          finally
          {
              fs.Close();
          }
          return "参数错误,打印失败!";
      }

      private bool RunCmd(string command)
      {
          //实例一个Process类，启动一个独立进程  
          Process p = new Process();
          //Process类有一个StartInfo属性，這個是ProcessStartInfo类，包括了一些属性和方法，下面我們用到了他的几个属性：  
          p.StartInfo.FileName = "cmd.exe";//设定程序名  
          p.StartInfo.Arguments = "/c " + command;//设定程式执行参数  
          p.StartInfo.UseShellExecute = false;//关闭Shell的使用  
          p.StartInfo.RedirectStandardInput = true;//重定向标准输入  
          p.StartInfo.RedirectStandardOutput = true;//重定向标准输出  
          p.StartInfo.RedirectStandardError = true;//重定向错误输出  
          p.StartInfo.CreateNoWindow = true;//设置不显示窗口  
          //p.StandardInput.WriteLine(command);//也可以用这种方式输入要执行的命令  
          //p.StandardInput.WriteLine("exit");//不过要记得加上Exit要不然下一行程式执行的時候会当机  
          try
          {
              p.Start();//开始进程  
              return true;
          }
          catch
          {
          }
          finally
          {
              if (p != null)
                  p.Close();
          }
          return false;
      }


      /* 
    ^XA                   ^XA指令块的开始      
    ^MD30                 ^MD是设置色带颜色的深度,取值范围从-30到30,上面的示意指令将颜色调到了最深.           
    ^LH60,10              ^LH是设置条码纸的边距的,这个东西在实际操作上来回试几次即可. 
    ^FO20,10              ^FO是设置条码左上角的位置的,这个对程序员应该很容易理解. 0,0代表完全不留边距. 
    ^ACN,18,10            ^ACN是设置字体的.因为在条码下方会显示该条码的内容,所以要设一下字体.这个跟条码无关. 
    ^BY1.4,3,50           ^BY是设置条码样式的,1.4是条码的缩放级别,3是条码中粗细柱的比例,50是条码高度. 
    ^BCN,,Y,N              ^BC是打印code128的指令,具体参数详见ZPL的说明书(百度云盘) 
    ^FD01008D004Q-0^FS    ^FD设置要打印的内容, ^FS表示换行. 
    ^XZ                   ^XZ指令块的开始     
     
StringBuilder builder = new StringBuilder();  
builder.AppendLine("^XA");  
builder.AppendLine("^MD30");  
builder.AppendLine("^LH60,10");  
builder.AppendLine("^FO20,10");  
builder.AppendLine("^ACN,18,10");  
builder.AppendLine("^BY1.4,3,50");  
builder.AppendLine("^BCN,,Y,N");  
builder.AppendLine("^FD01008D004Q-0^FS");  
builder.AppendLine("^XZ");  
  */

      /* 
     中文或其它复杂设计成图片，然后用ZPL命令发送给条码打印机打印              
    //定义字体   
    Font drawFont = new Font("Arial", 10, FontStyle.Bold, GraphicsUnit.Millimeter); 
    //生成图片 
    Bitmap img = CreateImage("出厂日期：" + DateTime.Now, drawFont); 
    var imgCode = ConvertImageToCode(img); 
    var t = ((img.Size.Width / 8 + ((img.Size.Width % 8 == 0) ? 0 : 1)) * img.Size.Height).ToString(); //图形中的总字节数 
    var w = (img.Size.Width / 8 + ((img.Size.Width % 8 == 0) ? 0 : 1)).ToString(); //每行的字节数 
    string zpl = string.Format("~DGR:imgName.GRF,{0},{1},{2}", t, w, imgCode); //发送给打印机 
*/

      /// <summary>  
      /// 生成Bitmap  
      /// </summary>  
      /// <param name="data">字符串</param>  
      /// <param name="f">文本格式</param>  
      /// <returns></returns>  
      protected Bitmap CreateImage(string data, Font f)
      {
          if (string.IsNullOrEmpty(data))
              return null;
          var txt = new TextBox();
          txt.Text = data;
          txt.Font = f;
          //txt.PreferredSize.Height只能取到一行的高度(连边距)   
          //所以需要乘以行数, 但是必须先减掉边距, 乘了以后,再把边距加上.   
          //5是目测的边距   
          var image = new Bitmap(txt.PreferredSize.Width, (txt.PreferredSize.Height - 5) * txt.Lines.Length + 5);
          var g = Graphics.FromImage(image);
          var b = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Black, Color.Black, 1.2f, true);
          g.Clear(System.Drawing.Color.White);
          g.DrawString(data, f, b, 1, 1);
          return image;
      }

      /// <summary>  
      /// 序列化图片  
      /// </summary>  
      /// <param name="img">Bitmap</param>  
      /// <returns></returns>  
      protected string ConvertImageToCode(Bitmap img)
      {
          var sb = new StringBuilder();
          long clr = 0, n = 0;
          int b = 0;
          for (int i = 0; i < img.Size.Height; i++)
          {
              for (int j = 0; j < img.Size.Width; j++)
              {
                  b = b * 2;
                  clr = img.GetPixel(j, i).ToArgb();
                  string s = clr.ToString("X");

                  if (s.Substring(s.Length - 6, 6).CompareTo("BBBBBB") < 0)
                  {
                      b++;
                  }
                  n++;
                  if (j == (img.Size.Width - 1))
                  {
                      if (n < 8)
                      {
                          b = b * (2 ^ (8 - (int)n));

                          sb.Append(b.ToString("X").PadLeft(2, '0'));
                          b = 0;
                          n = 0;
                      }
                  }
                  if (n >= 8)
                  {
                      sb.Append(b.ToString("X").PadLeft(2, '0'));
                      b = 0;
                      n = 0;
                  }
              }
              sb.Append(System.Environment.NewLine);
          }
          return sb.ToString();
      }   
  } 
 



}